package com.atguigu.tingshu.order.service.impl;

import com.alibaba.fastjson.JSON;
import com.atguigu.tingshu.account.client.UserAccountFeignClient;
import com.atguigu.tingshu.album.client.AlbumInfoFeignClient;
import com.atguigu.tingshu.album.client.TrackInfoFeignClient;
import com.atguigu.tingshu.common.constant.SystemConstant;
import com.atguigu.tingshu.common.execption.GuiguException;
import com.atguigu.tingshu.common.rabbit.constant.MqConst;
import com.atguigu.tingshu.common.rabbit.service.RabbitService;
import com.atguigu.tingshu.common.result.Result;
import com.atguigu.tingshu.common.result.ResultCodeEnum;
import com.atguigu.tingshu.model.album.AlbumInfo;
import com.atguigu.tingshu.model.album.TrackInfo;
import com.atguigu.tingshu.model.order.OrderDerate;
import com.atguigu.tingshu.model.order.OrderDetail;
import com.atguigu.tingshu.model.order.OrderInfo;
import com.atguigu.tingshu.model.user.VipServiceConfig;
import com.atguigu.tingshu.order.helper.SignHelper;
import com.atguigu.tingshu.order.mapper.OrderDerateMapper;
import com.atguigu.tingshu.order.mapper.OrderDetailMapper;
import com.atguigu.tingshu.order.mapper.OrderInfoMapper;
import com.atguigu.tingshu.order.service.OrderInfoService;
import com.atguigu.tingshu.tingshu.vo.account.AccountDeductVo;
import com.atguigu.tingshu.user.client.UserInfoFeignClient;
import com.atguigu.tingshu.user.client.VipServiceConfigFeignClient;
import com.atguigu.tingshu.vo.order.OrderDerateVo;
import com.atguigu.tingshu.vo.order.OrderDetailVo;
import com.atguigu.tingshu.vo.order.OrderInfoVo;
import com.atguigu.tingshu.vo.order.TradeVo;
import com.atguigu.tingshu.vo.user.UserInfoVo;
import com.atguigu.tingshu.vo.user.UserPaidRecordVo;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Assert;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Slf4j
@Service
@SuppressWarnings({"all"})
public class OrderInfoServiceImpl extends ServiceImpl<OrderInfoMapper, OrderInfo> implements OrderInfoService {

    @Autowired
    private OrderInfoMapper orderInfoMapper;
    @Autowired
    private UserInfoFeignClient userInfoFeignClient;
    @Autowired
    private AlbumInfoFeignClient albumInfoFeignClient;
    @Autowired
    private VipServiceConfigFeignClient vipServiceConfigFeignClient;
    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private TrackInfoFeignClient trackInfoFeignClient;
    @Autowired
    private RabbitService rabbitService;
    @Autowired
    private UserAccountFeignClient userAccountFeignClient;
    @Autowired
    private OrderDetailMapper orderDetailMapper;
    @Autowired
    private OrderDerateMapper orderDerateMapper;

    @Override
    public OrderInfo getOrderInfo(String orderNo) {
        OrderInfo orderInfo = orderInfoMapper.selectOne(new LambdaQueryWrapper<OrderInfo>().eq(OrderInfo::getOrderNo, orderNo));
        orderInfo.setPayWayName(SystemConstant.ORDER_PAY_ACCOUNT.equals(orderInfo.getPayWay())?"余额支付":"在线支付");
        return orderInfo;
    }

    @Override
    public IPage<OrderInfo> findUserPage(Page<OrderInfo> pageParam, Long userId) {
        IPage<OrderInfo> infoIPage =  orderInfoMapper.selectUserPage(pageParam,userId);
        infoIPage.getRecords().forEach(item->{
            item.setOrderStatusName(getOrderStatusName(item.getOrderStatus()));
            item.setPayWayName(getPayWayName(item.getPayWay()));
        });
        return infoIPage;
    }

    private String getPayWayName(String payWay) {
        if (SystemConstant.ORDER_PAY_WAY_ALIPAY.equals(payWay)){
            return "余额支付";
        }else {
            return "微信支付";
        }

    }

    private String getOrderStatusName(String orderStatus) {
        //  声明订单状态名称
        String orderStatusName = "";
        //  判断
        if (SystemConstant.ORDER_STATUS_UNPAID.equals(orderStatus)){
            orderStatusName = "未支付";
        } else if (SystemConstant.ORDER_STATUS_PAID.equals(orderStatus)) {
            orderStatusName = "已支付";
        } else {
            orderStatusName = "已取消";
        }
        //  返回
        return orderStatusName;
    }

    @Override
    public void orderCancel(String orderNo) {
        OrderInfo orderInfo = orderInfoMapper.selectOne(new LambdaQueryWrapper<OrderInfo>().eq(OrderInfo::getOrderNo, orderNo));
        if (null != orderInfo && SystemConstant.ORDER_STATUS_UNPAID.equals(orderInfo.getOrderStatus())){
            orderInfo.setOrderStatus(SystemConstant.ORDER_STATUS_CANCEL);
            orderInfoMapper.updateById(orderInfo);
        }
    }

    /**
     * 提交订单方法
     *
     * @param orderInfoVo 订单信息封装对象，包含订单详细信息
     * @param userId 用户ID，用于关联订单与用户
     * @return 返回订单号，用于后续的订单追踪和管理
     *
     * 该方法的主要职责是处理订单提交过程，包括验证订单信息、生成订单号、
     * 处理在线支付和余额支付的逻辑差异，并与Redis和RabbitMQ交互以支持
     * 分布式环境下的订单处理和取消机制。
     */
    @Override
    public String submitOrder(OrderInfoVo orderInfoVo, Long userId) {
        // 将订单信息转换为Map对象，便于后续处理
        Map map = JSON.parseObject(JSON.toJSONString(orderInfoVo), Map.class);
        // 添加支付方式到Map对象中，此处未设置具体支付方式
        map.put("payWay","");
        SignHelper.checkSign(map);

        // 获取交易号，用于验证订单的唯一性
        String tradeNo = orderInfoVo.getTradeNo();
        // 如果交易号为空，抛出非法请求异常
        if (StringUtils.isEmpty(tradeNo)){
            throw  new GuiguException(ResultCodeEnum.ILLEGAL_REQUEST);
        }

        // 构造Redis键名，用于后续的订单重复提交检查
//        String tradeNoKey = "tradeNo:"+userId+":"+tradeNo;
        String tradeNokey = "user:trade:" + userId;
        // Lua脚本，用于原子性地检查和删除Redis中的交易号
        String script = "if(redis.call('get', KEYS[1]) == ARGV[1]) then return redis.call('del', KEYS[1]) else return 0 end";
        // 执行Lua脚本，检查是否可以删除交易号，以防止重复提交
        Boolean flag = (Boolean) redisTemplate.execute(new DefaultRedisScript<>(script, Boolean.class), Arrays.asList(tradeNokey), tradeNo);
        // 如果删除失败，说明订单已存在，抛出重复提交异常
        if (!flag) {
            // 不能重复提交订单！
            throw new GuiguException(ResultCodeEnum.ORDER_SUBMIT_REPEAT);
        }

        // 生成订单号，用于唯一标识订单
        String orderNo = UUID.randomUUID().toString().replaceAll("-","");

        // 判断付费的方式
        if(!SystemConstant.ORDER_PAY_ACCOUNT.equals(orderInfoVo.getPayWay())){
            // 在线支付逻辑
            OrderInfo orderInfo = this.saveOrder(userId, orderNo, orderInfoVo);
            // 发送延迟消息，用于用户取消订单
            rabbitService.sendDealyMessage(MqConst.EXCHANGE_ORDER,MqConst.ROUTING_CANCEL_ORDER,orderNo,MqConst.CANCEL_ORDER_DELAY_TIME);
        }else {
            // 检查账户可用金额
            // 封装远程调用的传参对象
            AccountDeductVo accountDeductVo = new AccountDeductVo();
            accountDeductVo.setOrderNo(orderNo);
            accountDeductVo.setUserId(userId);
            accountDeductVo.setContent(orderInfoVo.getOrderDetailVoList().get(0).getItemName());
            accountDeductVo.setAmount(orderInfoVo.getOrderAmount());
            // 远程调用用户账户服务，检查并扣减余额
            Result result = userAccountFeignClient.checkAndDeduct(accountDeductVo);
            if (200 != result.getCode()){
                throw new RuntimeException("扣减库存失败");
            }
            // 更新订单状态为已支付
            OrderInfo orderInfo = this.saveOrder(userId,orderNo,orderInfoVo);
            orderInfo.setOrderStatus(SystemConstant.ORDER_STATUS_PAID);
            orderInfo.setUpdateTime(new Date());
            orderInfoMapper.update(orderInfo,new LambdaQueryWrapper<OrderInfo>().eq(OrderInfo::getOrderNo,orderNo));
            // 保存用户购买记录
            UserPaidRecordVo userPaidRecordVo = new UserPaidRecordVo();
            userPaidRecordVo.setUserId(userId);
            userPaidRecordVo.setItemType(orderInfo.getItemType());
            userPaidRecordVo.setOrderNo(orderNo);
            List<Long> itemIdList = orderInfoVo.getOrderDetailVoList().stream().map(OrderDetailVo::getItemId).collect(Collectors.toList());
            userPaidRecordVo.setItemIdList(itemIdList);
            // 远程调用用户信息服务，保存用户购买记录
            Result userResult = userInfoFeignClient.savePaidRecord(userPaidRecordVo);
            if (200 != userResult.getCode()){
                throw new GuiguException(211,"新增购买记录失败");
            }

            return orderNo;
        }

        return orderNo;
    }

    /**
     * 保存订单信息，包括订单基本信息、订单减免信息和订单详细信息
     * 此方法在执行过程中如果遇到异常，将会进行回滚
     *
     * @param userId 用户ID，用于关联用户和订单
     * @param orderNo 订单编号，用于唯一标识一个订单
     * @param orderInfoVo 订单信息的传输对象，包含订单的基本信息、减免信息和详细信息
     */
    @Transactional(rollbackFor = Exception.class)
    private OrderInfo saveOrder(Long userId, String orderNo, OrderInfoVo orderInfoVo) {

        // 创建一个新的订单信息对象，并从传输对象中复制属性
        OrderInfo orderInfo = new OrderInfo();
        BeanUtils.copyProperties(orderInfoVo,orderInfo);
        // 设置订单的用户ID
        orderInfo.setUserId(userId);
        // 设置订单标题为第一个订单详情项的名称
        orderInfo.setOrderTitle(orderInfoVo.getOrderDetailVoList().get(0).getItemName());
        // 设置订单编号
        orderInfo.setOrderNo(orderNo);
        // 插入订单信息到数据库
        orderInfoMapper.insert(orderInfo);

        // 获取订单减免信息列表
        List<OrderDerateVo> orderDerateVoList = orderInfoVo.getOrderDerateVoList();
        // 如果订单减免信息列表不为空，则遍历列表并保存每个减免信息
        if (!CollectionUtils.isEmpty(orderDerateVoList)){
            for (OrderDerateVo orderDerateVo : orderDerateVoList) {
                OrderDerate orderDerate = new OrderDerate();
                BeanUtils.copyProperties(orderDerateVo,orderDerate);
                // 设置减免信息的订单ID
                orderDerate.setOrderId(orderInfo.getId());
                // 插入减免信息到数据库
                orderDerateMapper.insert(orderDerate);
            }
        }

        // 获取订单详细信息列表
        List<OrderDetailVo> orderDetailVoList = orderInfoVo.getOrderDetailVoList();
        // 如果订单详细信息列表不为空，则遍历列表并保存每个详情项
        if (!CollectionUtils.isEmpty(orderDetailVoList)){
            for (OrderDetailVo orderDetailVo : orderDetailVoList) {
                OrderDetail orderDetail = new OrderDetail();
                BeanUtils.copyProperties(orderDetailVo,orderDetail);
                // 设置详情项的订单ID
                orderDetail.setOrderId(orderInfo.getId());
                // 插入详情项到数据库
                orderDetailMapper.insert(orderDetail);
            }
        }
        return orderInfo;
    }

    /**
     * 交易方法，根据用户选择的商品类型计算订单信息
     *
     * @param tradeVo 包含用户选择的商品信息
     * @param userId  用户ID
     * @return 返回订单信息对象
     */
    @Override
    public OrderInfoVo trade(TradeVo tradeVo, Long userId) {

        // 调用用户信息服务获取用户信息
        Result<UserInfoVo> userInfoVoResult = userInfoFeignClient.getUserInfo(userId);
        Assert.notNull(userInfoVoResult, "远程调用失败");
        UserInfoVo userInfoVo = userInfoVoResult.getData();
        Assert.notNull(userInfoVo, "远程调用失败");

        // 初始化订单相关金额
        BigDecimal originalAmount = new BigDecimal("0.00");
        BigDecimal derateAmount = new BigDecimal("0.00");
        BigDecimal orderAmount = new BigDecimal("0.00");
        // 初始化订单详情和优惠列表
        List<OrderDetailVo> orderDetailVolist = new ArrayList<>();
        List<OrderDerateVo> orderDerateVoList = new ArrayList<>();

        // 根据商品类型处理订单
        if (tradeVo.getItemType().equals(SystemConstant.ORDER_ITEM_TYPE_ALBUM)) {

            // 检查专辑是否为已付费专辑
            Result<Boolean> isPaidAlbumResult = userInfoFeignClient.isPaidAlbum(tradeVo.getItemId());
            Assert.notNull(isPaidAlbumResult, "远程调用失败");
            Boolean isPaidAlbum = isPaidAlbumResult.getData();
            Assert.notNull(isPaidAlbum, "远程调用失败");
            if (isPaidAlbum) {

                throw new GuiguException(ResultCodeEnum.REPEAT_BUY_ERROR);
            }

            // 获取专辑信息
            Result<AlbumInfo> albumInfoResult = albumInfoFeignClient.getAlbumInfoById(tradeVo.getItemId());
            Assert.notNull(albumInfoResult, "远程调用失败");
            AlbumInfo albumInfo = albumInfoResult.getData();
            Assert.notNull(albumInfo, "远程调用失败");

            // 根据用户是否为VIP计算订单金额
            if (0 == userInfoVo.getIsVip().intValue() || (1 == userInfoVo.getIsVip() && userInfoVo.getVipExpireTime().before(new Date()))) {

                originalAmount = albumInfo.getPrice();

                if (-1 != albumInfo.getDiscount().intValue()) {

                    derateAmount = albumInfo.getPrice().multiply(new BigDecimal(10).subtract(albumInfo.getDiscount())).divide(new BigDecimal(10), 2, RoundingMode.HALF_UP);
                }
            } else {
                // 用户是VIP
                originalAmount = albumInfo.getPrice();
                if (-1 != albumInfo.getVipDiscount().intValue()) {
                    derateAmount = albumInfo.getPrice().multiply(new BigDecimal(10).subtract(albumInfo.getVipDiscount())).divide(new BigDecimal(10), 2, RoundingMode.HALF_UP);

                }
            }
            orderAmount = originalAmount.subtract(derateAmount);

            // 如果有优惠，添加到优惠列表
            if (0 != originalAmount.subtract(orderAmount).doubleValue()) {
                OrderDerateVo orderDerateVo = new OrderDerateVo();
                orderDerateVo.setDerateAmount(originalAmount.subtract(orderAmount));
                orderDerateVo.setDerateType(SystemConstant.ORDER_DERATE_ALBUM_DISCOUNT);
                orderDerateVo.setRemarks("");
                orderDerateVoList.add(orderDerateVo);
            }

            // 构建订单详情
            OrderDetailVo orderDetailVo = new OrderDetailVo();
            orderDetailVo.setItemId(tradeVo.getItemId());
            orderDetailVo.setItemName(albumInfo.getAlbumTitle());
            orderDetailVo.setItemPrice(albumInfo.getPrice());
            orderDetailVo.setItemUrl(albumInfo.getCoverUrl());
            orderDetailVolist.add(orderDetailVo);

        } else if (tradeVo.getItemType().equals(SystemConstant.ORDER_ITEM_TYPE_TRACK)) {
            if (tradeVo.getTrackCount() < 0) {
                throw new GuiguException(ResultCodeEnum.ARGUMENT_VALID_ERROR);
            }

            //获取下单的声音信息列表
            Result<List<TrackInfo>> trackInfoListResult = trackInfoFeignClient.findPaidTrackInfoList(tradeVo.getItemId(), tradeVo.getTrackCount());
            Assert.notNull(trackInfoListResult, "远程调用失败");
            List<TrackInfo> trackInfoList = trackInfoListResult.getData();
            Assert.notNull(trackInfoList, "远程调用失败");
            //根据下单的声音列表找出专辑id然后远程调用获取专辑信息
            Result<AlbumInfo> albumInfoByIdResult = albumInfoFeignClient.getAlbumInfoById(trackInfoList.get(0).getAlbumId());
            Assert.notNull(albumInfoByIdResult, "远程调用失败");
            AlbumInfo albumInfo = albumInfoByIdResult.getData();
            Assert.notNull(albumInfo, "远程调用失败");
            //原价：需要付费的专辑*专辑单价
            originalAmount = tradeVo.getTrackCount().intValue() > 0 ? albumInfo.getPrice()
                    .multiply(new BigDecimal(tradeVo.getTrackCount())) : albumInfo.getPrice();
            orderAmount = originalAmount;

            //订单详情赋值
            orderDetailVolist = trackInfoList.stream().map(trackInfo -> {
                OrderDetailVo orderDetailVo = new OrderDetailVo();
                orderDetailVo.setItemUrl(trackInfo.getCoverUrl());
                orderDetailVo.setItemPrice(albumInfo.getPrice());
                orderDetailVo.setItemName(trackInfo.getTrackTitle());
                orderDetailVo.setItemId(trackInfo.getId());
                return orderDetailVo;
            }).collect(Collectors.toList());


        } else {
            // 处理VIP服务类型商品的逻辑

            Result<VipServiceConfig> vipServiceConfigResult = vipServiceConfigFeignClient.getVipServiceConfig(tradeVo.getItemId());
            Assert.notNull(vipServiceConfigResult, "远程调用失败");
            VipServiceConfig vipServiceConfig = vipServiceConfigResult.getData();
            Assert.notNull(vipServiceConfig, "远程调用失败");

            originalAmount = vipServiceConfig.getPrice();
            derateAmount = vipServiceConfig.getPrice().subtract(vipServiceConfig.getDiscountPrice());
            orderAmount = originalAmount.subtract(derateAmount);

            // 如果有优惠，添加到优惠列表
            if (0 != originalAmount.subtract(orderAmount).doubleValue()) {
                OrderDerateVo orderDerateVo = new OrderDerateVo();
                orderDerateVo.setDerateAmount(originalAmount.subtract(orderAmount));
                orderDerateVo.setDerateType(SystemConstant.ORDER_DERATE_VIP_SERVICE_DISCOUNT);
                orderDerateVo.setRemarks("");
                orderDerateVoList.add(orderDerateVo);
            }

            // 构建订单详情
            OrderDetailVo orderDetailVo = new OrderDetailVo();
            orderDetailVo.setItemId(tradeVo.getItemId());
            orderDetailVo.setItemName(vipServiceConfig.getName());
            orderDetailVo.setItemPrice(vipServiceConfig.getPrice());
            orderDetailVo.setItemUrl(vipServiceConfig.getImageUrl());
            orderDetailVolist.add(orderDetailVo);
        }

        // 构建订单信息
        OrderInfoVo orderInfoVo = new OrderInfoVo();
        orderInfoVo.setOriginalAmount(originalAmount);
        orderInfoVo.setDerateAmount(derateAmount);
        orderInfoVo.setOrderAmount(orderAmount);
        orderInfoVo.setOrderDetailVoList(orderDetailVolist);
        orderInfoVo.setOrderDerateVoList(orderDerateVoList);

        // 生成交易编号并存储到Redis
        String tradeNokey = "user:trade:" + userId;
        String tradeNo = UUID.randomUUID().toString().replaceAll("-", "");
        redisTemplate.opsForValue().set(tradeNokey, tradeNo, 15, TimeUnit.MINUTES);
        orderInfoVo.setTradeNo(tradeNo);

        // 设置订单其他信息
        orderInfoVo.setItemType(tradeVo.getItemType());
        orderInfoVo.setPayWay("");
        orderInfoVo.setTimestamp(SignHelper.getTimestamp());

        // 生成签名
        Map parameterMap = JSON.parseObject(JSON.toJSONString(orderInfoVo), Map.class);
        String sign = SignHelper.getSign(parameterMap);
        orderInfoVo.setSign(sign);

        return orderInfoVo;
    }
}
